package com.cloudpark.common.util;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;

public class JsonBinder {

    private static Logger logger = Logger.getLogger(JsonBinder.class);

    private ObjectMapper mapper;
    private Map map;

    private static String ERROR_RESULT = "{\"code\":-1}";

    @SuppressWarnings("serial")
    private void setAnnotationIntrospector() {
        mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
            public Object findFilterId(AnnotatedClass ac) {
                return ac.getName();
            }
        });
    }

    public JsonBinder(Include include) {
        map = new HashMap();
        mapper = new ObjectMapper();
        // 设置输出包含的属性
        mapper.setSerializationInclusion(include);
        // 设置输入时忽略JSON字符串中存在而Java对象实际没有的属性
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
        mapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, false);
        mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
        // false);

        /**/

        this.setDateFormat("yyyy-MM-dd HH:mm:ss");

        mapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {

            @Override
            public void serialize(Object value, JsonGenerator jg, SerializerProvider sp) throws IOException,
                    JsonProcessingException {
                jg.writeString("");
            }
        });

    }

    public static JsonBinder buildBinder() {
        return new JsonBinder(Include.NON_NULL);
    }

    public static JsonBinder buildBinder(boolean flag) {
        if (flag) {
            return new JsonBinder(Include.ALWAYS);
        } else {
            return new JsonBinder(Include.NON_NULL);
        }
    }

    /**
     * @Title toJavaBean
     * @Description TODO(json字符串转换为对象)
     * @param <T>
     * @param jsonString
     * @param clazz
     * @return T
     * @author TangAiBo
     * @date 2015-4-13 下午02:19:13
     */
    public <T> T toJavaBean(String jsonString, Class<T> clazz) {
        if (StringUtils.isEmpty(jsonString)) {
            return null;
        }
        try {
            return mapper.readValue(jsonString, clazz);
        } catch (IOException e) {
            logger.warn("parse json string error:" + jsonString, e);
            return null;
        }
    }

    /**
     * @Title toJavaBeanList
     * @Description TODO(json字符串转换为list对象集合) 还可以 直接使用
     *              JsonBinder.getMapper().readValue(String jsonString, new
     *              TypeReference<List<T>>(){})方式
     * @param <T>
     * @param content
     * @param typeReference
     * @throws IOException
     * @return List<T>
     * @author TangAiBo
     * @date 2015-4-13 下午01:50:46
     */
    public <T> List<T> toJavaBeanList(String jsonString, TypeReference<List<T>> typeReference) throws IOException {
        if (StringUtils.isEmpty(jsonString)) {
            return null;
        }
        try {
            return mapper.readValue(jsonString, typeReference);
        } catch (JsonParseException e) {
            logger.error("json字符串转化为 list失败,原因:" + e.toString());
            throw new RuntimeException("json字符串转化为 list失败");
        } catch (JsonMappingException e) {
            logger.error("json字符串转化为 list失败,原因" + e.toString());
            throw new JsonMappingException("json字符串转化为 list失败");
        } catch (IOException e) {
            logger.error("json字符串转化为 list失败,原因" + e.toString());
            throw new IOException("json字符串转化为 list失败");
        }
    }

    public String toJSONWithExcludeProperties(Pagination pageBean, String pattern, String... properties) {
        this.setDateFormat(pattern);
        return this.toJSONWithExcludeProperties(pageBean, properties);
    }

    public String toJSON(Pagination pageBean, String pattern, String... properties) {

        this.setDateFormat(pattern);
        return this.toJSON(pageBean, properties);

    }


    public String toJSONWithExcludeProperties(Pagination pageBean, String... properties) {
        try {
            map.put("code", 1);
            map.put("page", pageBean.getCurrentPage());
            map.put("totalPage", pageBean.getTotalPages());
            map.put("data", pageBean.getResultList());
            String paramString = "";
            if (pageBean.getTotalPages() > 0) {
                if (pageBean.getResultList() instanceof List) {
                    paramString = pageBean.getResultList().get(0).getClass().getName();
                } else {
                    paramString = pageBean.getResultList().getClass().getName();
                }
            }
            this.setAnnotationIntrospector();
            return mapper.writer(
                    new SimpleFilterProvider().addFilter(paramString,
                            SimpleBeanPropertyFilter.serializeAllExcept(properties))).writeValueAsString(map);

        } catch (JsonGenerationException e) {
            logger.warn("write to json string error:" + pageBean, e);
        } catch (JsonMappingException e) {
            logger.warn("write to json string error:" + pageBean, e);
        } catch (IOException e) {
            logger.warn("write to json string error:" + pageBean, e);
        }
        return ERROR_RESULT;
    }

    public String toJSON(Pagination pageBean, String... properties) {
        try {
            map.put("code", 1);
            map.put("page", pageBean.getCurrentPage());
            map.put("totalPage", pageBean.getTotalPages());
            map.put("data", pageBean.getResultList());
            String paramString = "";
            if (pageBean.getTotalPages() > 0) {
                if (pageBean.getResultList() instanceof List) {
                    paramString = pageBean.getResultList().get(0).getClass().getName();
                } else {
                    paramString = pageBean.getResultList().getClass().getName();
                }
            }
            this.setAnnotationIntrospector();
            return mapper.writer(
                    new SimpleFilterProvider().addFilter(paramString,
                            SimpleBeanPropertyFilter.filterOutAllExcept(properties))).writeValueAsString(map);

        } catch (JsonGenerationException e) {
            logger.warn("write to json string error:" + pageBean, e);
        } catch (JsonMappingException e) {
            logger.warn("write to json string error:" + pageBean, e);
        } catch (IOException e) {
            logger.warn("write to json string error:" + pageBean, e);
        }
        return ERROR_RESULT;
    }

    /**
     * 如果对象为Null,返回"null". 如果集合为空集合,返回"[]".
     */
    public String toJSON(Object object) {

        // try {
        // map.put("code", 1);
        // map.put("data", object);
        // return mapper.writeValueAsString(map);
        // } catch (IOException e) {
        // e.printStackTrace();
        // logger.warn("write to json string error:" + object, e);
        // }
        // return ERROR_RESULT;
        return toJSONWithExcludeProperties(object, new String[] {});
    }

    public String toJSON(Object object, String pattern) {
        this.setDateFormat(pattern);
        return this.toJSON(object);
    }

    public String toJSON(Object object, String... properties) {
        try {
            map.put("code", 1);
            map.put("data", object);
            if (null == object
                    || ((object instanceof List || object instanceof ArrayList) && ((List) object).size() == 0)) {
                return mapper.writeValueAsString(map);
            } else {
                String paramString = "";
                if (object instanceof List || object instanceof ArrayList) {
                    paramString = ((List) object).get(0).getClass().getName();
                } else {
                    paramString = object.getClass().getName();
                }
                FilterProvider simpleFilterProvider = new SimpleFilterProvider().addFilter(paramString,
                        SimpleBeanPropertyFilter.filterOutAllExcept(properties));
                this.setAnnotationIntrospector();
                return mapper.writer(simpleFilterProvider).writeValueAsString(map);
            }

        } catch (JsonGenerationException e) {
            logger.warn("write to json string error:" + object, e);
        } catch (JsonMappingException e) {
            logger.warn("write to json string error:" + object, e);
        } catch (IOException e) {
            logger.warn("write to json string error:" + object, e);
        }
        return ERROR_RESULT;
    }

    public String toJSON() {
        map.put("code", 1);
        try {
            return mapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 
     * @param object
     * @param pattern
     * @param properties
     * @return
     * @throws JsonGenerationException
     * @throws JsonMappingException
     * @throws IOException
     */
    public String toJSON(Object object, String pattern, String... properties) {
        this.setDateFormat(pattern);
        return this.toJSON(object, properties);
    }

    public String toJSONWithExcludeProperties(Object object, String... properties) {
        try {
            map.put("code", 1);
            map.put("data", object);
            if (null == object
                    || ((object instanceof List || object instanceof ArrayList) && ((List) object).size() == 0)) {
                return mapper.writeValueAsString(map);
            } else {
                String paramString = "";
                if (object instanceof List || object instanceof ArrayList) {
                    paramString = ((List) object).get(0).getClass().getName();
                } else {
                    paramString = object.getClass().getName();
                }
                this.setAnnotationIntrospector();
                return mapper.writer(
                        new SimpleFilterProvider().addFilter(paramString,
                                SimpleBeanPropertyFilter.serializeAllExcept(properties))).writeValueAsString(map);
            }
        } catch (JsonGenerationException e) {
            logger.warn("write to json string error:" + object, e);
        } catch (JsonMappingException e) {
            logger.warn("write to json string error:" + object, e);
        } catch (IOException e) {
            logger.warn("write to json string error:" + object, e);
        }
        return ERROR_RESULT;
    }

    /**
     * 
     * @param object
     * @param pattern
     * @param properties
     * @return
     * @throws JsonGenerationException
     * @throws JsonMappingException
     * @throws IOException
     */
    public String toJSONWithExcludeProperties(Object object, String pattern, String... properties) {
        this.setDateFormat(pattern);
        return this.toJSONWithExcludeProperties(object, properties);
    }

    /**
     * 设置转换日期类型的format pattern,如果不设置默认打印Timestamp毫秒数.
     */
    public void setDateFormat(String pattern) {
        if (StringUtils.isNotBlank(pattern)) {
            DateFormat df = new SimpleDateFormat(pattern);
            mapper.setDateFormat(df);
        }
    }

    /**
     * 取出Mapper做进一步的设置或使用其他序列化API.
     */
    public ObjectMapper getMapper() {
        return mapper;
    }

    /**
     * 取出map
     * 
     * @return
     */
    public Map getMap() {
        return map;
    }

    /**
     * 
     * @Title: toJSONWithExcludeProperties
     * @Description: map2json
     * @param map
     * @return 设定文件 String 返回类型
     * @throws
     * @author xz
     */
    public String toJSON(Map map) {
        try {
            map.put("code", 1);
            return mapper.writeValueAsString(map);
        } catch (JsonGenerationException e) {
            logger.warn("write to json string error:" + map, e);
        } catch (JsonMappingException e) {
            logger.warn("write to json string error:" + map, e);
        } catch (IOException e) {
            logger.warn("write to json string error:" + map, e);
        }
        return ERROR_RESULT;
    }

    /**
     * 
     * @Title: toJSONWithExcludeProperties
     * @Description: map2json ,不返回code
     * @param map
     * @return 设定文件 String 返回类型
     * @throws
     * @author xz
     */
    public String toJSONWithoutCode(Map map) {
        try {
            return mapper.writeValueAsString(map);
        } catch (JsonGenerationException e) {
            logger.warn("write to json string error:" + map, e);
        } catch (JsonMappingException e) {
            logger.warn("write to json string error:" + map, e);
        } catch (IOException e) {
            logger.warn("write to json string error:" + map, e);
        }
        return ERROR_RESULT;
    }

    public String toJSONDataTables(Pagination pageBean, int draw, String... properties) {
        try {
            map.put("draw", draw);
            map.put("recordsTotal", pageBean.getTotalRows());
            map.put("recordsFiltered", pageBean.getTotalRows());
            map.put("data", pageBean.getResultList());
            String paramString = "";
            if (pageBean.getTotalPages() > 0) {
                if (pageBean.getResultList() instanceof List) {
                    paramString = pageBean.getResultList().get(0).getClass().getName();
                } else {
                    paramString = pageBean.getResultList().getClass().getName();
                }
            }
            this.setAnnotationIntrospector();

            return mapper.writer(
                    new SimpleFilterProvider().addFilter(paramString,
                            SimpleBeanPropertyFilter.filterOutAllExcept(properties))).writeValueAsString(map);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return ERROR_RESULT;
    }

}
